package org.coffeebreaks.james.mailets.directory;

/**
 * Copyright 2008 Jerome Lacoste.
 *
 * Licensed under the Apache License, Version 2.0 (the "License" );
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.apache.mailet.GenericMailet;
import org.apache.mailet.Mail;
import org.apache.mailet.MailetConfig;
import org.apache.mailet.MailAddress;
//import org.coffeebreaks.util.ClassLoaderUtil;

import javax.mail.MessagingException;
import javax.mail.internet.ParseException;
import java.util.*;

/**
 * Mailet that expands mail directory group and user recipients into real recipients.
 * <p/>
 * There are three modes:
 * <ul>
 * <li>GROUPS_AND_USERS (default): searches for groups first, users second</li>
 * <li>GROUPS: only replaces groups</li>
 * <li>USERS: only replaces users</li>
 * </ul>
 * <p/>
 * 
 * This implementation currently only supports Crowd and requires a crowd.properties file to be in the classpath. Hopefully this will be fixed one day.
 * <p/>
 * Removes duplicate emails if a recipient is listed multiple times.
 *
 * @author jerome@coffeebreaks.org
 * @see DirectoryService
 */
public class MapDirectoryMembersToEmails extends GenericMailet {

  private DirectoryService directoryService;

  /**
   * The various configuration modes. Determines what to search and in which order.
   */
  public enum Mode {
    GROUPS_AND_USERS, GROUPS, USERS 
  }

  private Mode mode;
  private boolean debug;

  /**
   * 2 options available:
   * <ul>
   * <li>mode: {@link #Mode} </li>
   * <li>debug: set to true to have a verbose output (<strong>>deprecated</strong>)</li>
   * </ul>
   */
  public void init(MailetConfig mailetConfig) throws MessagingException {
    super.init(mailetConfig);

    // this parameter is hopefully temporary
    setDebug("true".equals(getInitParameter("debug", "false")));
    setMode(Mode.valueOf(getInitParameter("mode", Mode.GROUPS_AND_USERS.name())));
    debug("MapDirectoryGroupToEmails:" + mode);

    // note here we could improve by allowing to hardcode the crowd configuration in the james config
    // but the current API of the SecurityServerClient doesn't seem to allow for programatic
    // specification/override of the properties
    setDirectoryService(new CrowdDirectoryService());
  }
  
  void setMode(Mode mode) {
    this.mode = mode;
  }
  
  /** @deprecated this will be replaced with a real logger implementation */
  void setDebug(boolean debug) {
    this.debug = debug;
  }

  void setDirectoryService(DirectoryService directoryService) {
    this.directoryService = directoryService;
  }

  private void debug(final String message) {
    if (debug) {
      System.out.println(message);
    }
  }

  private void error(final String message, Throwable t) {
    System.err.println(message);
    t.printStackTrace(System.err);
  }

  public void service(Mail mail) throws MessagingException {
    // we probably want to only serve users or groups known by the directory
    try {
      Collection recipients = mail.getRecipients();
      Set<MailAddress> newMailRecipients = new HashSet<MailAddress>();
      for (Object recipient : recipients) {
        MailAddress m = (MailAddress) recipient;
        boolean replaced = replaceMailRecipient(m, newMailRecipients, mode);
        if (!replaced) {
          newMailRecipients.add(m);
        }
      }
      debug("MapDirectoryUsersToEmails: Replaced " + recipients + " with " + newMailRecipients);
      mail.setRecipients(newMailRecipients);
    } catch (Throwable t) {
      //ClassLoaderUtil.debugClassLoader("Context class loader:", Thread.currentThread().getContextClassLoader());
      //ClassLoaderUtil.debugClassLoader("Class class loader:", MapDirectoryMembersToEmails.class.getClassLoader());
      //ClassLoaderUtil.debugClassLoader("System class loader:", ClassLoader.getSystemClassLoader());

      error("Failure overriding recipients " + t.getMessage(), t);
    }
  }

  private boolean replaceMailRecipient(MailAddress originalMailAddress, Set<MailAddress> newMailRecipients, Mode currentMode) throws ParseException {
    boolean replaced = false;
    switch (currentMode) {
      case USERS:
        String userEmail = directoryService.getUserEmail(originalMailAddress.getUser());
        if (userEmail != null) {
          newMailRecipients.add(new MailAddress(userEmail));
          replaced = true;
        }
        break;
      case GROUPS:
        String[] emails = directoryService.getGroupEmails(originalMailAddress.getUser());
        if (emails != null) {
          for (String email : emails) {
            newMailRecipients.add(new MailAddress(email));
          }
          replaced = true;
        }
        break;
      case GROUPS_AND_USERS:
        // try replacing groups then users
        replaced = replaceMailRecipient(originalMailAddress, newMailRecipients, Mode.GROUPS)
                || replaceMailRecipient(originalMailAddress, newMailRecipients, Mode.USERS);
        break;
    }
    return replaced;
  }
}
